/*
 * Created by Jaren at 2022/10/31 15:20
 */

import 'dart:typed_data';

import 'package:binary/binary.dart';
import 'package:inmotion_protocol/base/vehicle_data.dart';
import 'package:inmotion_protocol/common/compact.dart';
import 'package:inmotion_protocol/common/product_model.dart';
import 'package:inmotion_protocol/common/product_version.dart';
import 'package:inmotion_protocol/lorin/bean/base/base.dart';
import 'package:inmotion_protocol/lorin/bean/base/bms_info.dart';
import 'package:inmotion_protocol/lorin/bean/common/date.dart';
import 'package:inmotion_protocol/lorin/util.dart';
import 'package:inmotion_protocol/util/byte_util.dart';

class BMSFixedInfo extends Base<BMSFixedInfo> {
  Uint16 format = Uint16.zero;
  Uint16 designedCapacity = Uint16.zero;
  Date mfgDate = Date.emptyDate;
  String appVersion = "";
  String blVersion = "";
  String hwVersion = "";
  String bmsSn = "";
  String batterySn = "";

  BMSFixedInfo(ProductModel model, VersionWrapper version)
      : super(model, version);

  @override
  void initCompact() {
    compact = _DefaultBMSFixedInfo<BMSFixedInfo>();
  }

  @override
  String toString() {
    return 'BMSFixedInfo{format: $format, designedCapacity: $designedCapacity, mfgDate: $mfgDate, appVersion: $appVersion, blVersion: $blVersion, hwVersion: $hwVersion, bmsSn: $bmsSn, batterySn: $batterySn}';
  }
}

/// default bms fixed info implement
class _DefaultBMSFixedInfo<T extends BMSFixedInfo> extends Compact<T> {
  @override
  void fromBytes(T output, Uint8ClampedList bytes, int offset) {
    output.format = ByteUtil.bytes2Uint16Le(bytes, offset);
    offset += 2;
    output.designedCapacity = ByteUtil.bytes2Uint16Le(bytes, offset);
    offset += 2;
    // 保留2bytes
    offset += 2;
    output.mfgDate = Date.fromBytes(bytes, offset: offset);
    offset += Date.length;

    output.appVersion = Util.formatVersion(bytes, offset, 4);
    offset += 4;
    output.blVersion = Util.formatVersion(bytes, offset, 4);
    offset += 4;
    output.hwVersion = Util.formatHwVersion(bytes, offset, 2);
    offset += 2;

    // 保留2bytes
    offset += 2;
    output.bmsSn = Util.parseSn(bytes, offset);
    offset += output.bmsSn.length + 1;
    output.batterySn = Util.parseSn(bytes, offset);
  }

  @override
  int length() => 60;
}

class DefaultBMSFixedInfoDelegate<T extends BMSFixedInfo>
    extends _DefaultBMSFixedInfo<T> {}

class BMSAllRealTimeInfo extends Base<BMSAllRealTimeInfo> {
  Uint16 format = Uint16.zero;
  Datetime datetime = Datetime.emptyDate;
  BMSGeneralInfo? _generalInfo;
  BMSState? _state;
  BMSWarning? _warning;
  BMSProtection? _protection;
  BMSError? _error;

  bool initByImplements = false;

  BMSAllRealTimeInfo(ProductModel model, VersionWrapper version,
      [this.initByImplements = false])
      : super(model, version) {
    if (!initByImplements) {
      _generalInfo = BMSGeneralInfo(model, version);
      _state = BMSState(model, version);
      _warning = BMSWarning(model, version);
      _protection = BMSProtection(model, version);
      _error = BMSError(model, version);
    }
  }

  BMSGeneralInfo get generalInfo => _generalInfo!;

  set generalInfo(BMSGeneralInfo generalInfo) => _generalInfo = generalInfo;

  BMSState get state => _state!;

  set state(BMSState state) => _state = state;

  BMSWarning get warning => _warning!;

  set warning(BMSWarning warning) => _warning = warning;

  BMSProtection get protection => _protection!;

  set protection(BMSProtection protection) => _protection = protection;

  BMSError get error => _error!;

  set error(BMSError error) => _error = error;

  @override
  void initCompact() {
    compact = DefaultBMSAllRealTimeInfo();
  }

  @override
  String toString() {
    return 'BMSAllRealTimeInfo{format: $format, datetime: $datetime, generalInfo: $generalInfo(), state: $state, warning: $warning, protection: $protection, error: $error}';
  }
}

class DefaultBMSAllRealTimeInfo<T extends BMSAllRealTimeInfo> extends Compact<T> {
  @override
  void fromBytes(
      T output, Uint8ClampedList bytes, int offset) {
    output.format = ByteUtil.bytes2Uint16Le(bytes, offset);
    offset += 2;

    output.datetime = Datetime.fromBytes(bytes, offset: offset);
    offset += Datetime.length;

    output.generalInfo.fromBytes(bytes, offset);
    offset += output.generalInfo.length();

    output.state.fromBytes(bytes, offset);
    offset += output.state.length();

    output.warning.fromBytes(bytes, offset);
    offset += output.warning.length();

    output.protection.fromBytes(bytes, offset);
    offset += output.protection.length();

    output.error.fromBytes(bytes, offset);
    offset += output.error.length();
  }

  @override
  int length() => 53;
}

class BMSGeneralInfo extends Base<BMSGeneralInfo> {
  Uint16 voltage = Uint16.zero;
  Int16 chargeCurrent = Int16.zero;
  Int16 dischargeCurrent = Int16.zero;
  Uint16 fullCapacity = Uint16.zero;
  Uint16 remainCapacity = Uint16.zero;
  Uint16 fullChargeCycles = Uint16.zero;
  Uint16 maxCellVoltage = Uint16.zero;
  Uint16 minCellVoltage = Uint16.zero;

  Int8 chargeMosTemp = Int8.zero;
  Int8 dischargeMosTemp = Int8.zero;
  Int8 boardTemp1 = Int8.zero;
  Int8 boardTemp2 = Int8.zero;

  Int8List cellsTemp = Int8List(8);

  Uint8 relativeHumidity = Uint8.zero;

  Uint16 powerOffCountdown = Uint16.zero;

  BMSGeneralInfo(ProductModel model, VersionWrapper version)
      : super(model, version);

  @override
  void initCompact() {
    compact = DefaultBMSGeneralInfoCompact();
    _setCellTempSensorCount(cellTempSensorCount());
  }

  @override
  String toString() {
    return 'BMSGeneralInfo{voltage: $voltage, chargeCurrent: $chargeCurrent, dischargeCurrent: $dischargeCurrent, fullCapacity: $fullCapacity, remainCapacity: $remainCapacity, fullChargeCycles: $fullChargeCycles, maxCellVoltage: $maxCellVoltage, minCellVoltage: $minCellVoltage, chargeMosTemp: $chargeMosTemp, dischargeMosTemp: $dischargeMosTemp, boardTemp1: $boardTemp1, boardTemp2: $boardTemp2, cellsTemp: $cellsTemp, relativeHumidity: $relativeHumidity, powerOffCountdown: $powerOffCountdown}';
  }

  int cellTempSensorCount() => 8;

  void _setCellTempSensorCount(int count) {
    cellsTemp = Int8List(count);
  }
}

class DefaultBMSGeneralInfoCompact<T extends BMSGeneralInfo> extends Compact<T> {
  @override
  void fromBytes(T output, Uint8ClampedList bytes, int offset) {
    output.voltage = ByteUtil.bytes2Uint16Le(bytes, offset);
    offset += 2;
    output.chargeCurrent = ByteUtil.bytes2Int16Le(bytes, offset);
    offset += 2;
    output.dischargeCurrent = ByteUtil.bytes2Int16Le(bytes, offset);
    offset += 2;
    output.fullCapacity = ByteUtil.bytes2Uint16Le(bytes, offset);
    offset += 2;
    output.remainCapacity = ByteUtil.bytes2Uint16Le(bytes, offset);
    offset += 2;
    output.fullChargeCycles = ByteUtil.bytes2Uint16Le(bytes, offset);
    offset += 2;
    output.maxCellVoltage = ByteUtil.bytes2Uint16Le(bytes, offset);
    offset += 2;
    output.minCellVoltage = ByteUtil.bytes2Uint16Le(bytes, offset);
    offset += 2;

    // 保留2byte
    offset += 2;

    output.chargeMosTemp =
        ByteUtil.toInt8(ByteUtil.toInt8Int(bytes[offset]) + 80);
    offset++;
    output.dischargeMosTemp =
        ByteUtil.toInt8(ByteUtil.toInt8Int(bytes[offset]) + 80);
    offset++;
    output.boardTemp1 = ByteUtil.toInt8(ByteUtil.toInt8Int(bytes[offset]) + 80);
    offset++;
    output.boardTemp2 = ByteUtil.toInt8(ByteUtil.toInt8Int(bytes[offset]) + 80);
    offset++;
    for (var i = 0; i < output.cellsTemp.length; i++) {
      output.cellsTemp[i] =
          ByteUtil.toInt8(ByteUtil.toInt8Int(bytes[offset + i]) + 80).value;
    }
    offset += output.cellsTemp.length;

    output.relativeHumidity = ByteUtil.toUint8(bytes[offset]);
    offset++;

    // 保留1byte
    offset++;

    output.powerOffCountdown = ByteUtil.bytes2Uint16Le(bytes, offset);

    // 剩余2byte保留
  }

  @override
  int length() => 36;
}

abstract class BMSCellsVoltage extends Base<BMSCellsVoltage>
    with AbsBMSCellsVoltage {
  Uint16List? _voltages;

  BMSCellsVoltage(ProductModel model, VersionWrapper version)
      : super(model, version) {
    _voltages = Uint16List(batteryCount());
  }

  @override
  Uint16List get voltages => _voltages!;

  @override
  void initCompact() {
    compact = _DefaultBMSCellsVoltage();
  }

  @override
  int length() {
    /// 每个电芯电压2个byte
    return batteryCount() * 2;
  }

  int batteryCount();
}

class _DefaultBMSCellsVoltage extends Compact<BMSCellsVoltage> {
  @override
  void fromBytes(BMSCellsVoltage output, Uint8ClampedList bytes, int offset) {
    for (var i = 0; i < output.voltages.length; i++, offset += 2) {
      if (bytes.length > offset) {
        output.voltages[i] = ByteUtil.bytes2Uint16Le(bytes, offset).value;
      }
    }
  }

  @override
  int length() => 0;
}

class BMSProtection extends Base<BMSProtection> {
  Uint4 shortCircuit = Uint4.zero;
  Uint4 packOverCharge = Uint4.zero;
  Uint4 packOverDischarge = Uint4.zero;
  Uint4 cellOverCharge = Uint4.zero;
  Uint4 cellOverDischarge = Uint4.zero;
  Uint4 chargeOverCurrent = Uint4.zero;
  Uint4 chargeMosOverTemp = Uint4.zero;
  Uint4 chargeCellOverTemp = Uint4.zero;

  Uint4 chargeCellLowTemp = Uint4.zero;
  Uint4 dischargeOverCurrent = Uint4.zero;
  Uint4 dischargeMosOverTemp = Uint4.zero;
  Uint4 dischargeCellOverTemp = Uint4.zero;
  Uint4 dischargeCellLowTemp = Uint4.zero;
  Uint4 overHumidityTemp = Uint4.zero;
  Uint4 associatedCharge = Uint4.zero;
  Uint4 associatedDischarge = Uint4.zero;

  BMSProtection(ProductModel model, VersionWrapper version)
      : super(model, version);

  @override
  String toString() {
    return 'BMSProtection{shortCircuit: $shortCircuit, packOverCharge: $packOverCharge, packOverDischarge: $packOverDischarge, cellOverCharge: $cellOverCharge, cellOverDischarge: $cellOverDischarge, chargeOverCurrent: $chargeOverCurrent, chargeMosOverTemp: $chargeMosOverTemp, chargeCellOverTemp: $chargeCellOverTemp, chargeCellLowTemp: $chargeCellLowTemp, dischargeOverCurrent: $dischargeOverCurrent, dischargeMosOverTemp: $dischargeMosOverTemp, dischargeCellOverTemp: $dischargeCellOverTemp, dischargeCellLowTemp: $dischargeCellLowTemp, overHumidityTemp: $overHumidityTemp, associatedCharge: $associatedCharge, associatedDischarge: $associatedDischarge}';
  }

  @override
  void initCompact() {
    compact = DefaultBMSProtectionCompact<BMSProtection>();
  }
}

class DefaultBMSProtectionCompact<T extends BMSProtection> extends Compact<T> {
  @override
  void fromBytes(T output, Uint8ClampedList bytes, int offset) {
    output.shortCircuit = ByteUtil.bitValue(bytes[offset], 0, 1);
    output.packOverCharge = ByteUtil.bitValue(bytes[offset], 1, 1);
    output.packOverDischarge = ByteUtil.bitValue(bytes[offset], 2, 1);
    output.cellOverCharge = ByteUtil.bitValue(bytes[offset], 3, 1);
    output.cellOverDischarge = ByteUtil.bitValue(bytes[offset], 4, 1);
    output.chargeOverCurrent = ByteUtil.bitValue(bytes[offset], 5, 1);
    output.chargeMosOverTemp = ByteUtil.bitValue(bytes[offset], 6, 1);
    output.chargeCellOverTemp = ByteUtil.bitValue(bytes[offset], 7, 1);

    offset++;

    output.chargeCellLowTemp = ByteUtil.bitValue(bytes[offset], 0, 1);
    output.dischargeOverCurrent = ByteUtil.bitValue(bytes[offset], 1, 1);
    output.dischargeMosOverTemp = ByteUtil.bitValue(bytes[offset], 2, 1);
    output.dischargeCellOverTemp = ByteUtil.bitValue(bytes[offset], 3, 1);
    output.dischargeCellLowTemp = ByteUtil.bitValue(bytes[offset], 4, 1);
    output.overHumidityTemp = ByteUtil.bitValue(bytes[offset], 5, 1);
    output.associatedCharge = ByteUtil.bitValue(bytes[offset], 6, 1);
    output.associatedDischarge = ByteUtil.bitValue(bytes[offset], 7, 1);
    offset++;
  }

  @override
  int length() => 3;
}

class BMSError extends Base<BMSError> {
  Uint4 voltageSample = Uint4.zero;
  Uint4 chargeCurrentSample = Uint4.zero;
  Uint4 dischargeCurrentSample = Uint4.zero;
  Uint4 tempSensor = Uint4.zero;
  Uint4 relativeHumiditySensor = Uint4.zero;
  Uint4 chargeMos = Uint4.zero;
  Uint4 dischargeMos = Uint4.zero;
  Uint4 afe = Uint4.zero;

  Uint4 cell = Uint4.zero;
  Uint4 mcu = Uint4.zero;

  BMSError(ProductModel model, VersionWrapper version) : super(model, version);

  @override
  String toString() {
    return 'BMSError{voltageSample: $voltageSample, chargeCurrentSample: $chargeCurrentSample, dischargeCurrentSample: $dischargeCurrentSample, tempSensor: $tempSensor, relativeHumiditySensor: $relativeHumiditySensor, chargeMos: $chargeMos, dischargeMos: $dischargeMos, afe: $afe, cell: $cell, mcu: $mcu}';
  }

  @override
  void initCompact() {
    compact = DefaultBMSErrorCompact();
  }
}

class DefaultBMSErrorCompact<T extends BMSError> extends Compact<T> {
  @override
  void fromBytes(T output, Uint8ClampedList bytes, int offset) {
    output.voltageSample = ByteUtil.bitValue(bytes[offset], 0, 1);
    output.chargeCurrentSample = ByteUtil.bitValue(bytes[offset], 1, 1);
    output.dischargeCurrentSample = ByteUtil.bitValue(bytes[offset], 2, 1);
    output.tempSensor = ByteUtil.bitValue(bytes[offset], 3, 1);
    output.relativeHumiditySensor = ByteUtil.bitValue(bytes[offset], 4, 1);
    output.chargeMos = ByteUtil.bitValue(bytes[offset], 5, 1);
    output.dischargeMos = ByteUtil.bitValue(bytes[offset], 6, 1);
    output.afe = ByteUtil.bitValue(bytes[offset], 7, 1);

    offset++;

    output.cell = ByteUtil.bitValue(bytes[offset], 0, 1);
    output.mcu = ByteUtil.bitValue(bytes[offset], 1, 1);
  }

  @override
  int length() => 3;
}

class BMSWarning extends Base<BMSWarning> {
  Uint4 lowSoc = Uint4.zero;
  Uint4 packOverCharge = Uint4.zero;
  Uint4 packOverDischarge = Uint4.zero;
  Uint4 cellOverCharge = Uint4.zero;
  Uint4 cellOverDischarge = Uint4.zero;
  Uint4 chargeOverCurrent = Uint4.zero;
  Uint4 chargeMosOverTemp = Uint4.zero;
  Uint4 chargeCellOverTemp = Uint4.zero;

  Uint4 chargeCellLowTemp = Uint4.zero;
  Uint4 dischargeOverCurrent = Uint4.zero;
  Uint4 dischargeMosOverTemp = Uint4.zero;
  Uint4 dischargeCellOverTemp = Uint4.zero;
  Uint4 dischargeCellLowTemp = Uint4.zero;
  Uint4 overHumidity = Uint4.zero;

  BMSWarning(ProductModel model, VersionWrapper version)
      : super(model, version);

  @override
  void initCompact() {
    compact = DefaultBMSWarningCompact<BMSWarning>();
  }

  @override
  String toString() {
    return 'BMSWarning{lowSoc: $lowSoc, packOverCharge: $packOverCharge, packOverDischarge: $packOverDischarge, cellOverCharge: $cellOverCharge, cellOverDischarge: $cellOverDischarge, chargeOverCurrent: $chargeOverCurrent, chargeMosOverTemp: $chargeMosOverTemp, chargeCellOverTemp: $chargeCellOverTemp, chargeCellLowTemp: $chargeCellLowTemp, dischargeOverCurrent: $dischargeOverCurrent, dischargeMosOverTemp: $dischargeMosOverTemp, dischargeCellOverTemp: $dischargeCellOverTemp, dischargeCellLowTemp: $dischargeCellLowTemp, overHumidity: $overHumidity';
  }
}

class DefaultBMSWarningCompact<T extends BMSWarning> extends Compact<T> {
  @override
  void fromBytes(T output, Uint8ClampedList bytes, int offset) {
    output.lowSoc = ByteUtil.bitValue(bytes[offset], 0, 1);
    output.packOverCharge = ByteUtil.bitValue(bytes[offset], 1, 1);
    output.packOverDischarge = ByteUtil.bitValue(bytes[offset], 2, 1);
    output.cellOverCharge = ByteUtil.bitValue(bytes[offset], 3, 1);
    output.cellOverDischarge = ByteUtil.bitValue(bytes[offset], 4, 1);
    output.chargeOverCurrent = ByteUtil.bitValue(bytes[offset], 5, 1);
    output.chargeMosOverTemp = ByteUtil.bitValue(bytes[offset], 6, 1);
    output.chargeCellOverTemp = ByteUtil.bitValue(bytes[offset], 7, 1);
    offset++;

    output.chargeCellLowTemp = ByteUtil.bitValue(bytes[offset], 0, 1);
    output.dischargeOverCurrent = ByteUtil.bitValue(bytes[offset], 1, 1);
    output.dischargeMosOverTemp = ByteUtil.bitValue(bytes[offset], 2, 1);
    output.dischargeCellOverTemp = ByteUtil.bitValue(bytes[offset], 3, 1);
    output.dischargeCellLowTemp = ByteUtil.bitValue(bytes[offset], 4, 1);
    output.overHumidity = ByteUtil.bitValue(bytes[offset], 5, 1);
    // 保留1bit
    // 保留1byte
  }

  @override
  int length() => 3;
}

class BMSState extends Base<BMSState> {
  Uint4 innerState = Uint4.zero;
  Uint4 chargerPlugInState = Uint4.zero;
  Uint4 chargingState = Uint4.zero;
  Uint4 dischargingState = Uint4.zero;
  Uint4 fullChargedState = Uint4.zero;

  Uint4 keyActiveState = Uint4.zero;
  Uint4 holdActiveState = Uint4.zero;

  Uint4 chargeMosState = Uint4.zero;
  Uint4 dischargeMosState = Uint4.zero;

  Uint4 activeHeatState = Uint4.zero;

  Uint4 communicationState = Uint4.zero;

  Uint4 cellBalancingState = Uint4.zero;

  BMSState(ProductModel model, VersionWrapper version) : super(model, version);

  @override
  void initCompact() {
    compact = _DefaultBMSStateCompact();
  }

  @override
  String toString() {
    return 'BMSState{innerState: $innerState, chargerPlugInState: $chargerPlugInState, chargingState: $chargingState, dischargingState: $dischargingState, fullChargedState: $fullChargedState, keyActiveState: $keyActiveState, holdActiveState: $holdActiveState, chargeMosState: $chargeMosState, dischargeMosState: $dischargeMosState, activeHeatState: $activeHeatState, communicationState: $communicationState, cellBalancingState: $cellBalancingState}';
  }
}

class _DefaultBMSStateCompact extends Compact<BMSState> {
  @override
  void fromBytes(BMSState output, Uint8ClampedList bytes, int offset) {
    output.innerState = ByteUtil.bitValue(bytes[offset], 0, 4);
    output.chargerPlugInState = ByteUtil.bitValue(bytes[offset], 4, 1);
    output.chargingState = ByteUtil.bitValue(bytes[offset], 5, 1);
    output.dischargingState = ByteUtil.bitValue(bytes[offset], 6, 1);
    output.fullChargedState = ByteUtil.bitValue(bytes[offset], 7, 1);
    offset += 1;

    output.keyActiveState = ByteUtil.bitValue(bytes[offset], 0, 1);

    output.holdActiveState = ByteUtil.bitValue(bytes[offset], 1, 1);
    output.chargeMosState = ByteUtil.bitValue(bytes[offset], 2, 1);
    output.dischargeMosState = ByteUtil.bitValue(bytes[offset], 3, 1);
    output.activeHeatState = ByteUtil.bitValue(bytes[offset], 4, 1);
    output.communicationState = ByteUtil.bitValue(bytes[offset], 5, 1);
    output.cellBalancingState = ByteUtil.bitValue(bytes[offset], 6, 1);
    // 保留1bit

    // 保留1byte
  }

  @override
  int length() {
    return 3;
  }
}

class BMSLog extends AbsBMSLog {
  static const int length = 128;

  Uint8ClampedList bytes = Uint8ClampedList(length);

  static BMSLog fromBytes(Uint8ClampedList bytes,
      {int offset = 0, BMSLog? output}) {
    output ??= BMSLog();
    ByteUtil.copy(bytes, offset, output.bytes, 0, bytes.length - offset);

    return output;
  }

  bool isValid() {
    return true;
  }
}

/// 可被软件识别的电池电芯类型<br/>
/// 枚举名称中的首个B无实际含义，如[B50GB]表示电芯名称为50GB
enum BatteryCellType {
  B50GB('50GB'),
  B50S('50S');

  final String name;

  const BatteryCellType(this.name);
}
